package org.esprit.ocm.server.metier.ec2.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.esprit.ocm.client.rpc.ec2.ImageService;
import org.esprit.ocm.dto.ec2.impl.ImageDescriptionDto;
import org.esprit.ocm.dto.ec2.impl.InstanceDescriptionDto;
import org.esprit.ocm.dto.impl.AwsCredentialsDto;
import org.esprit.ocm.dto.impl.ServerDto;

import com.xerox.amazonws.ec2.ImageDescription;
import com.xerox.amazonws.ec2.InstanceType;
import com.xerox.amazonws.ec2.Jec2;
import com.xerox.amazonws.ec2.LaunchConfiguration;
import com.xerox.amazonws.ec2.ReservationDescription;
import com.xerox.amazonws.ec2.ReservationDescription.Instance;

public class ImageServiceImpl extends BaseServiceImpl implements ImageService {

	/**
	 * Lists available EC2 AMIs that you could start an instance of.
	 * 
	 * Note that we have to work with arrays here because this class is going to
	 * be exposed as a Web service through Axis2 which does not support Lists.
	 * 
	 * @param all
	 *            get to true to display all public AMIs, set to false to list
	 *            only your own AMIs.
	 * @return Array of Images or null, if no images were found or credentials
	 *         were wrong.
	 */
	public List<ImageDescriptionDto> listImages(boolean all, ServerDto server, AwsCredentialsDto credentials) {

		// initialize the interface
		Jec2 ec2 = this.initConnection(server, credentials);

		List<ImageDescription> result = new ArrayList<ImageDescription>();
		List<ImageDescriptionDto> resultMapping = new ArrayList<ImageDescriptionDto>();

		try {
			if (all) {
				result = ec2.describeImages(new String[] {});// .toArray(new
																// ImageDescription[0]);
			} else {
				ArrayList<String> list = new ArrayList<String>();
				// list.add(this.getCredentials().getUserID());

				result = ec2.describeImagesByOwner(list);

			}

			// return result.toArray(new ImageDescription[0]);

			for (ImageDescription i : result) {

				ImageDescriptionDto idm = new ImageDescriptionDto();
				
				idm.setImageId(i.getImageId());
				idm.setArchitecture(i.getArchitecture());
				idm.setDescription(i.getDescription());
				idm.setImageLocation(i.getImageLocation());
				idm.setImageState(i.getImageState());
				idm.setImageOwnerId(i.getImageOwnerId());
				idm.setKernelId(i.getKernelId());
				idm.setRamdiskId(i.getRamdiskId());
				idm.setImageType(i.getImageType());
				
				resultMapping.add(idm);
			}
			return resultMapping;

		} catch (Exception ex) {
			Logger logger = Logger.getLogger(this.getClass().getName());
			logger.log(Level.SEVERE, "Listing images failed: " + ex.getMessage());

			// ex.printStackTrace();

			return null;
		}

	}

	/**
	 * Starts an instance with the given parameters.
	 * 
	 * @param imageId
	 *            AMI-Id of the instance to start.
	 * @param groups
	 *            List of groups.
	 * @param keyName
	 *            Name of the keypair to use.
	 * @param userData
	 *            User data for this instance (see
	 *            http://docs.amazonwebservices.
	 *            com/AWSEC2/2008-05-05/DeveloperGuide
	 *            /index.html?AESDG-chapter-instancedata.html).
	 * @param instanceType
	 *            EC2 Type of the instance
	 * @param count
	 *            number of instances to launch
	 * @param availabilityZone
	 *            Availability zone for instance.
	 * @param kernelId
	 *            KernelId to use. May be empty.
	 * @param ramdiskId
	 *            RamdiskId to use. May be empty.
	 * @return Instance or null in case of error.
	 */
//	public ReservationDescription runInstance(String imageId, String[] groups, String keyName, byte[] userData, InstanceType instanceType, int count,
//			String availabilityZone, String kernelId, String ramdiskId, ServerDto server, AwsCredentialsDto credentials) {
//
//		// initialize the interface
//		Jec2 ec2 = this.initConnection(server, credentials);
//
//		List<String> groupList = Arrays.asList(groups);
//
//		try {
//			// ReservationDescription d = ec2.runInstances(imageId,count, count,
//			// groupList, userData, keyName,instanceType);
//
//			LaunchConfiguration l = new LaunchConfiguration(imageId);
//
//			l.setAvailabilityZone(availabilityZone);
//			l.setInstanceType(instanceType);
//			l.setKernelId(kernelId);
//			l.setKeyName(keyName);
//			l.setMaxCount(count);
//			l.setMinCount(count);
//			l.setRamdiskId(ramdiskId);
//			l.setSecurityGroup(groupList);
//			l.setUserData(userData);
//
//			ReservationDescription d = ec2.runInstances(l);
//
//			return d;
//
//		} catch (Exception ex) {
//			Logger logger = Logger.getLogger(this.getClass().getName());
//			logger.log(Level.SEVERE, "Starting instance failed: " + ex.getMessage());
//
//			return null;
//		}
//
//	}

	/**
	 * Starts an instance with the given parameters.
	 * 
	 * @param imageId
	 *            AMI-Id of the instance to start.
	 * @param groups
	 *            List of groups.
	 * @param keyName
	 *            Name of the keypair to use.
	 * @param userData
	 *            User data for this instance (see
	 *            http://docs.amazonwebservices.
	 *            com/AWSEC2/2008-05-05/DeveloperGuide
	 *            /index.html?AESDG-chapter-instancedata.html).
	 * @param instanceType
	 *            EC2 Type of the instance
	 * @param count
	 *            number of instances to launch
	 * @param availabilityZone
	 *            Availability zone for instance.
	 * @param kernelId
	 *            KernelId to use. May be empty.
	 * @param ramdiskId
	 *            RamdiskId to use. May be empty.
	 * @return Instance or null in case of error.
	 */
	public String runInstance(String imageId, List<String> groups, String keyName, byte[] userData, String instanceType, int count,
			String availabilityZone, String kernelId, String ramdiskId, ServerDto server, AwsCredentialsDto credentials) {

		// initialize the interface
		Jec2 ec2 = this.initConnection(server, credentials);

//		List<String> groupList = Arrays.asList(groups);

		try {
			// ReservationDescription d = ec2.runInstances(imageId,count, count,
			// groupList, userData, keyName,instanceType);

			LaunchConfiguration l = new LaunchConfiguration(imageId);

			l.setAvailabilityZone(availabilityZone);
			l.setInstanceType(InstanceType.getTypeFromString(instanceType));
			l.setKernelId(kernelId);
			l.setRamdiskId(ramdiskId);
			l.setKeyName(keyName);
			l.setMaxCount(count);
			l.setMinCount(count);
			l.setSecurityGroup(groups);
			l.setUserData(userData);

			return ec2.runInstances(l).getRequestId();

			// return d;

		} catch (Exception ex) {
			Logger logger = Logger.getLogger(this.getClass().getName());
			logger.log(Level.SEVERE, "Starting instance failed: " + ex.getMessage());

			 return "Starting instance failed: " + ex.getMessage();
		}

	}

	/**
	 * Blocking start of an instance with the given parameters. Returns only if
	 * startup of instance (or instances, of count > 1) is complete (state is
	 * "running") or if an error occured.
	 * 
	 * @param imageId
	 *            AMI-Id of the instance to start.
	 * @param groups
	 *            List of groups.
	 * @param keyName
	 *            Name of the keypair to use.
	 * @param instanceType
	 *            EC2 Type of the instance
	 * @param count
	 *            number of instances to start with in ReservationDescription
	 * @return Instance(s) (if count > 1) or null in case of error.
	 */
	// public Instance[] runInstanceBlocking(String imageId, String[] groups,
	// String keyName, byte[] userData, InstanceType instanceType, int count,
	// String availabilityZone, String kernelId, String ramdiskId, Jec2 ec2) {
	//
	// ReservationDescription d = runInstance(imageId, groups, keyName,
	// userData, instanceType, count, availabilityZone, kernelId, ramdiskId,
	// ec2);
	//
	// if (d == null)
	// return null;
	//
	// List<Instance> result = new ArrayList<Instance>();
	//
	// for (com.xerox.amazonws.ec2.ReservationDescription.Instance i :
	// d.getInstances()) {
	//
	// Instance inst = this.describeInstance(i.getInstanceId(), ec2);
	//
	// result.add(inst);
	//
	// }
	//
	// boolean started = false;
	// while (!started) {
	//
	// try {
	// Thread.sleep(2500);
	// } catch (Exception ex) {
	//
	// }
	//
	// // de.jw.cloud42.core.domain.Instanced =
	// // describeInstance(d.getInstances().get(0).getInstanceId());
	// started = true;
	//
	// for (int index = 0; index < result.size(); index++) {
	//
	// Instance i = this.describeInstance(result.get(index).getInstanceId(),
	// ec2);
	//
	// result.set(index, i);
	//
	// if (!i.getState().equals("running")) {
	//
	// started = false;
	// }
	// }
	//
	// }
	//
	// // sleep a short time until SSH etc. are ready, too
	// try {
	// Thread.sleep(15000);
	// } catch (Exception ex) {
	//
	// }
	//
	// return result.toArray(new Instance[0]);
	//
	// }

	/**
	 * Get or refresh current information on a running instance identified by
	 * its Id.
	 * 
	 * @param instanceId
	 * @return Instance holding the current properties of the instance or null
	 *         in case of error.
	 */
	// public Instance describeInstance(String instanceId, credentials) {
	//
	// List<String> params = new ArrayList<String>();
	// params.add(instanceId);
	//
	// // initialize the interface
	// Jec2 ec2 = this.initConnection();
	//
	// try {
	// List<ReservationDescription> result = ec2.describeInstances(params);
	//
	// if (result != null && result.size() == 1) {
	// int index = 0;
	// for (com.xerox.amazonws.ec2.ReservationDescription.Instance i :
	// result.get(0).getInstances()) {
	//
	// if (i.getInstanceId().equals(instanceId)) {
	// return new Instance(result.get(0), index);
	// }
	//
	// index++;
	// }
	// }
	//
	// } catch (Exception ex) {
	// Logger logger = Logger.getLogger(this.getClass().getName());
	// logger.log(Level.SEVERE, "Describing instance failed: " +
	// ex.getMessage());
	//
	// }
	//
	// return null;
	// }

	/**
	 * Stop a specific AMI instance.
	 * 
	 * @param instanceId
	 * @return TerminatingInstanceDescription or null in case of error.
	 */
	public void stopInstance(List<String> instanceId, ServerDto server, AwsCredentialsDto credentials) {

		// initialize the interface
		Jec2 ec2 = this.initConnection(server, credentials);

		try {

			ec2.terminateInstances(instanceId);
			
//			InstanceStateChangeDescription from = ec2.terminateInstances(new String[] { instanceId }).get(0);

//			InstanceStateChangeDescriptionMappingDto to = new InstanceStateChangeDescriptionMappingDto();
//			to.setInstanceId(from.getInstanceId());
//			to.setPrevState(from.getPreviousState());
//			to.setPrevStateCode(from.getPreviousStateCode());
//			to.setCurrentState(from.getCurrentState());
//			to.setCurrentStateCode(from.getCurrentStateCode());

			// return to;

		} catch (Exception ex) {
			Logger logger = Logger.getLogger(this.getClass().getName());
			logger.log(Level.SEVERE, "Terminating instance failed: " + ex.getMessage());

			// return null;
		}

	}

	/**
	 * Stop all existing instances.
	 * 
	 * @return List of TerminatingInstanceDescription or null in case of error
	 *         or if no instances were running.
	 */
//	 public TerminatingInstanceDescription[] stopAllInstances(ServerDto
//	 server, AwsCredentialsDto credentials) {
//	
//	 // get all existing instances and create a list of instanceIDs
//	 org.esprit.ocm.server.metier.impl.serv.Instance[] list =
//	 this.listInstances(ec2);
//	
//	 if (list == null || list.length == 0)
//	 return null;
//	
//	 List<String> instanceIds = new ArrayList<String>();
//	 for (org.esprit.ocm.server.metier.impl.serv.Instance i : list) {
//	
//	 instanceIds.add(i.getInstanceId());
//	
//	 }
//	
//	 // initialize the interface
//	 Jec2 ec2 = this.initConnection(server, credentials);
//	
//	 try {
//	
//	 return ec2.terminateInstances(instanceIds).toArray(new
//	 TerminatingInstanceDescription[0]);
//	
//	 } catch (Exception ex) {
//	 Logger logger = Logger.getLogger(this.getClass().getName());
//	 logger.log(Level.SEVERE, "Terminating all instances failed: " +
//	 ex.getMessage());
//	
//	 return null;
//	 }
//	
//	 }

	/**
	 * Reboot a given instance.
	 * 
	 * @param instanceId
	 */
	public void rebootInstance(List<String> instanceId, ServerDto server, AwsCredentialsDto credentials) {

		// initialize the interface
		Jec2 ec2 = this.initConnection(server, credentials);

		try {
			ec2.rebootInstances(instanceId);
//			ec2.rebootInstances(new String[] { instanceId });

		} catch (Exception ex) {
			Logger logger = Logger.getLogger(this.getClass().getName());
			logger.log(Level.SEVERE, "Rebooting instance failed: " + ex.getMessage());

		}

	}

	/**
	 * 
	 * List all instances.
	 * 
	 * @return List of AMIInstances (may be empty) or null in case of error.
	 */
	public List<InstanceDescriptionDto> listInstances(ServerDto server, AwsCredentialsDto credentials) {

		List<String> params = new ArrayList<String>();

		// initialize the interface
		Jec2 ec2 = this.initConnection(server, credentials);

		try {
			List<ReservationDescription> reservationDescription = ec2.describeInstances(params);
			List<InstanceDescriptionDto> result = new ArrayList<InstanceDescriptionDto>();

			if (reservationDescription != null) {

				for (ReservationDescription rd : reservationDescription) {

					InstanceDescriptionDto id = new InstanceDescriptionDto();
					
					id.setResId(rd.getReservationId());
					id.setOwner(rd.getOwner());
					
					String groups = "";
					for (String gr : rd.getGroups()) {
						groups += gr + "; ";
					}
					id.setGroups(groups);
					
					for (Instance i : rd.getInstances()) {

						id.setImageId(i.getImageId());
						id.setInstanceId(i.getInstanceId());
						id.setPrivateDnsName(i.getPrivateDnsName());
						id.setDnsName(i.getDnsName());
						id.setReason(i.getReason());
						id.setKeyName(i.getKeyName());
						id.setInstanceType(i.getInstanceType().toString());
						id.setLaunchTime(i.getLaunchTime().getTime().toLocaleString());
						id.setLaunchIndex(i.getLaunchIndex());
						id.setAvailabilityZone(i.getAvailabilityZone());
						id.setKernelId(i.getKernelId());
						id.setRamdiskId(i.getRamdiskId());
						id.setPlatform(i.getPlatform());
						id.setState(i.getState());
						id.setMonitoring(i.isMonitoring());

						result.add(id);
						// System.out.println(i.toString());
					}
				}
				
				return result;
			}
		} catch (Exception ex) {
			Logger logger = Logger.getLogger(this.getClass().getName());
			logger.log(Level.SEVERE, "Listing instances failed: " + ex.getMessage());

		}
		return null;

	}

}
